﻿using System;
using System.Linq;
using System.Xml.Linq;
using Cyberwolf.AppServices.Components;
using Cyberwolf.AppServices.ServiceDefinitions;
using Cyberwolf.Modules.PWCAPAdmin.Components;

namespace Cyberwolf.Modules.PWCAPAdmin.Components
{
    public class PWApplicationService
    {
        private PWApplicationServiceProxy proxy;

        public PWApplicationService(string serviceUrl)
        {
            proxy = new PWApplicationServiceProxy();
            proxy.Url = serviceUrl;
        }

        public bool KeepAliveSession()
        {
            var token = PWSecurityToken.Current;
            if (token == null) return false;

            try
            {
                if (string.IsNullOrEmpty(token.SecurityToken))
                {
                    try
                    {
                        var xDoc = XDocument.Parse(proxy.Authenticate(token.OrganizationURL, token.Username, token.Password));

                        if (xDoc.Element("RequestContext").Elements("Messages").Elements("Message").Elements("MessageCode").Any(m => "Sec_AuthFailed".Equals(m.Value)))
                        {
                            token.ErrorMessage = string.Join(", ", xDoc.Element("RequestContext").Elements("Messages").Elements("Message").Select(m => m.Element("MessageText").Value).ToArray());
                            return false;
                        }

                        var responseDoc = XDocument.Parse(xDoc.Element("RequestContext").Element("ResponseDocument").Value);
                        var ns = responseDoc.Root.Name.Namespace;

                        token.SecurityToken = responseDoc.Root.Element(ns + "SecurityToken").Value;
                        token.SecurityInfo.LogonName = responseDoc.Root.Element(ns + "SecurityInformation").Element(ns + "LogonName").Value;
                        token.SecurityInfo.Organization = responseDoc.Root.Element(ns + "SecurityInformation").Element(ns + "Organization").Value;
                        token.SecurityInfo.Application = responseDoc.Root.Element(ns + "SecurityInformation").Element(ns + "Application").Value;
                        token.SecurityInfo.PartyRole = responseDoc.Root.Element(ns + "SecurityInformation").Element(ns + "PartyRole").Value;
                        token.PartyInfo.FirstName = responseDoc.Root.Element(ns + "PartyInformation").Element(ns + "First_Name").Value;
                        token.PartyInfo.LastName = responseDoc.Root.Element(ns + "PartyInformation").Element(ns + "Last_Name").Value;
                        token.PartyRights = responseDoc.Root.Element(ns + "PartyRights").Elements(ns + "PartyRight").Select(n => n.Value).ToArray();
                        token.PWAPModules = responseDoc.Root.Element(ns + "PWAPModules").Elements(ns + "PWAPModule").Select(n =>
                            new PWSecurityToken.ModuleInformation
                                {
                                    ModuleName = n.Element(ns + "ModuleName").Value,
                                    ModuleTitle = n.Element(ns + "ModuleTitle").Value,
                                    Version = n.Element(ns + "ModuleVersion").Value
                                }).ToArray();
                        token.ErrorMessage = string.Empty;

                        return true;
                    }
                    catch (Exception e)
                    {
                        token.SecurityToken = null;
                        token.ErrorMessage = e.Message;
                        return false;
                    }
                }
                else
                {
                    try
                    {
                        var keepAliveResponse = proxy.KeepAliveSession(token.SecurityToken);
                        var keepAliveResponseDoc = XDocument.Parse(keepAliveResponse);

                        if ("Y".Equals(keepAliveResponseDoc.Element("RequestContext").Element("RequestInfo").Element("Success").Value))
                        {
                            return true;
                        }

                        // Keepalive failed, check why
                        if (keepAliveResponseDoc.Element("RequestContext").Element("Messages").Elements("Message").Elements("MessageCode").Any(n => "Req_InvSession".Equals(n.Value)))
                        {
                            PWSecurityToken.Current.SecurityToken = null;
                            if (this.KeepAliveSession())
                            {
                                token = PWSecurityToken.Current;
                                return true;
                            }
                            else
                            {
                                return false;
                            }
                        }

                        token.ErrorMessage = string.Join(", ", keepAliveResponseDoc.Element("RequestContext").Elements("Messages").Elements("Message").Select(m => m.ToString()).ToArray());
                        return false;
                    }
                    catch (Exception e)
                    {
                        token.ErrorMessage = e.Message;
                        return false;
                    }
                }
            }
            finally
            {
                PWSecurityToken.Current = token;
            }
        }

        public bool Logout(string securityToken)
        {
            try
            {
                proxy.LogOut(securityToken);

                return true;
            }
            catch
            {
                return false;
            }
        }

        public string GetApplicationCryptInfo(string securityToken)
        {
            var result = proxy.ProcessDSRequest(securityToken, "GetApplicationCryptInfo", string.Empty, ArgumentType.DataSet.ToString("d"));

            var context = ClientContext.CreateContext(result);

            if (!context.Success) throw new Exception(context.ToString());

            return context.ResponseDoc;
        }

        public string UpdateApplicationCryptInfo(string securityToken, string requestSource)
        {
            var result = proxy.ProcessDSRequest(securityToken, "UpdateApplicationCryptInfo", requestSource, ArgumentType.DataSet.ToString("d"));

            var context = ClientContext.CreateContext(result);

            if (!context.Success) throw new Exception(context.ToString());

            return context.ResponseDoc;
        }

        public string GetConnectionInfo(string securityToken)
        {
            var result = proxy.ProcessDSRequest(securityToken, "ConnectionsForUpdate", string.Empty, ArgumentType.DataSet.ToString("d"));

            var context = ClientContext.CreateContext(result);

            if (!context.Success) throw new Exception(context.ToString());

            return context.ResponseDoc;
        }

        public string GetSymmetricKey(string securityToken)
        {
            var result = proxy.ProcessDSRequest(securityToken, "GetSymmetricKey", string.Empty, ArgumentType.DataSet.ToString("d"));

            var context = ClientContext.CreateContext(result);

            if (!context.Success) throw new Exception(context.ToString());

            return context.ResponseDoc;
        }

        public string UpdateConnectionInfo(string securityToken, string requestSource)
        {
            var result = proxy.ProcessDSRequest(securityToken, "UpdateConnections", requestSource, ArgumentType.DataSet.ToString("d"));

            var context = ClientContext.CreateContext(result);

            if (!context.Success) throw new Exception(context.ToString());

            return context.ResponseDoc;
        }

        public string GetOrganizations(string securityToken)
        {
            var result = proxy.ProcessDSRequest(securityToken, "ProfileGetOrgList", string.Empty,
                ArgumentType.DataSet.ToString("d"));
            var context = ClientContext.CreateContext(result);

            if (!context.Success) throw new Exception(context.ToString());

            return context.ResponseDoc;
        }
    }
}